// 对象转换成查询字符串方法
function queryStringify(obj) {
  let str = ''

  for (let k in obj) {
    str += k + '=' + obj[k] + '&'
  }

  return str.slice(0, -1)
}

// 封装 ajax 的代码
// 目的: 为了发送 ajax 请求, 向服务器获取数据
// 使用: 给 开发人员 使用
function ajax(options = {}) {
  // options 应该是一个对象, 传递的是本次请求所有相关的信息
  // 给 options 设置了一个参数默认值, 表示当你没有传递 options 的时候, 使用空对象
  // 目的: 后期访问 options 内的内容的时候, 可以不报错
  // console.log('options : ', options)
  // 0. 从 options 内拿到所有的信息, 为了后面验证的时候使用方便
  // 解构赋值
  // 等价于 const url = options.url, method = options.method
  const { url, method, async, data, headers, dataType, success } = options

  // 1. 验证 options 内的信息
  // 1-1. 验证 options 内的 url 信息是否填写了
  if (!url) {
    // 表示 options 内没有 url 这一项内容
    // 手动制造一个错误
    // 语法: const err = new Error('错误信息')
    const err = new Error('url 为必填选项, 请传递此项内容, ^_^')
    // 把错误信息抛出到运行程序, 导致程序中断
    // throw 错误信息
    throw err
  }

  // 1-2. 验证 method 信息
  // 可以不写, 但是你要是写了, 必须得是 GET 或者 POST
  // 判断可以是 undefined, 也可以是 get 或者 post, 其他的都不行
  if (!(method === undefined || /^(get|post)$/i.test(method))) {
    // 代码来到这里, 说明 method 既不是 undefined 也不是 get 也不是 post
    // 手动抛出异常
    throw new Error('method 目前只接受 "GET" 或者 "POST" 方式, 请期待更新 (#^.^#)')
  }

  // 1-3. 验证 async 信息
  // 可以不写, 但是你要是写了, 必须得是 true 或者 false
  if (!(async === undefined || typeof async === 'boolean')) {
    // 代码来到这里, 说明 async 既不是 undefiend 也不是 布尔值
    throw new Error('async 需要传递一个 "Boolean" 类型数据呦 O(∩_∩)O哈哈~')
  }

  // 1-4. 验证 data 信息
  // 可以不写, 但是你要是写了, 得是 字符串类型, 也可以是对象数据类型
  if (!(data === undefined || typeof data === 'string' || Object.prototype.toString.call(data) === '[object Object]')) {
    // 代码来到这里, 说明 data 既不是 undefiend 也不是字符串, 也不是 对象数据类型
    throw new Error('data 需要传递一个 "String" 或者 "Object" 类型数据 o(*￣︶￣*)o')
  }

  // 1-5. 验证 headers 信息
  // 可以不写, 但是你要是写了, 得是 对象数据类型
  if (!(headers === undefined || Object.prototype.toString.call(headers) === '[object Object]')) {
    throw new Error('headers 需要传递一个 "Object" 类型数据 o(*￣︶￣*)o')
  }

  // 1-6. 验证 dataType 信息
  // 可以不写, 但是你要是写了, 必须是字符串 'string' 或者 'json'
  if (!(dataType === undefined || /^(json|string)$/i.test(dataType))) {
    throw new Error('dataType 目前只接受 "json" 或者 "string" 两种值')
  }

  // 1-7. 验证 success 信息
  if (!(success === undefined || typeof success === 'function')) {
    throw new Error('success 需要传递一个 "Function" 类型数据')
  }

  // 2. 准备一套默认值
  const _default = {
    // 代码来到这里, url 肯定是有内容的
    url: url,
    // 代码来到这里, method 要么是 undefined, 要么是 get 要么是 post
    method: method || 'GET',
    // 代码来到这里, async 要么是 undefined, 要么是 true 要么是 false
    // 判断如果不是 布尔值, 那么给一个 true, 如果是 布尔值, 那么直接使用布尔值
    // async: typeof async === 'boolean' ? async : true,
    // 扩展: 空值运算(ES2020) ??
    // 语法: 值 ?? 值, 只有左边为 空 的时候, 才会使用右边
    async: async ?? true,
    // 代码来到这里, data 要么是 undefined, 要么是 字符串, 要么是 对象
    data: data || '',
    // 代码来到这里, headers 要么是 undefined, 要么是 对象数据类型的时候
    // 因为 headers 默认值是 { 'content-type': 'application/x-www-form-urlencoded' }
    // 如果传递的是 { author: 'asdjhgasdg' } 表示什么意思 ?
    // 在 默认值的基础上, 加上一个 author 属性
    // 如果传递的是 { 'content-type': 'application/json', author: 'asdasd' }
    // 把 默认值的 content-type 修改了, 在加上一个 author 属性
    // 使用 ES6 的展开运算符
    headers: { 'content-type': 'application/x-www-form-urlencoded', ...headers },
    // 代码来到这里, dataType 要么是 undefined, 要么是 'json' 要么是 'string'
    dataType: dataType || 'string',
    // 代码来到这里, success 要么是 undefined, 要么是 函数
    success: success || function () {}
  }
  // 2-1. 调整一下 data 的值
  // 当 data 为 对象数据类型的时候, 需要转换成 查询字符串
  // 因为代码执行到这里, _default 只有两种可能了, 字符串 或者 对象
  if (typeof _default.data === 'object') {
    // 表示 data 是一个对象数据类型
    // 把 _default.data 数据转化成 查询字符串
    _default.data = queryStringify(_default.data)
  }
  // 2-2. 调整一下 url 的值
  // 如果你是 get 请求, 并且 _default.data 有内容, 那么我就直接拼接好
  // 代码执行到这里, _default.data, 要么是 '', 要么是有内容得字符串
  if (/^get$/i.test(_default.method) && _default.data) {
    // 这个 if 条件可以执行, 说明 method 是 get, data 有数据
    _default.url += '?' + _default.data
  }

  // console.log('默认值 : ', _default)

  // 3. 发送请求的代码
  const xhr = new XMLHttpRequest()

  xhr.open(_default.method, _default.url, _default.async)

  xhr.onload = function () {
    // 暂时直接打印结果
    const res = _default.dataType === 'string' ? xhr.responseText : JSON.parse(xhr.responseText)

    // 调用 _default.success 函数
    _default.success(res)
  }

  // 只要 headers 内有内容, 就设置到请求头上
  // _default.headers 内有多少个信息, 设置多少条请求头
  for (let k in _default.headers) {
    // k 就是每一个名字,
    // _default.headers[k] 就是每个值
    xhr.setRequestHeader(k, _default.headers[k])
  }

  // 分辨一下, 是 get 还是 post 请求
  /^get$/i.test(_default.method) ? xhr.send() : xhr.send(_default.data)
}
